package com.zwf;

import cn.hutool.core.util.StrUtil;
import com.zwf.bo.SearchBO;
import com.zwf.constant.FileConstant;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.StringReader;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件检索工具
 * 利用生成的文件索引查询文件内容，在检索时可以输入关键字
 * 不只是文件名，包括里面的文件内容也可以匹配关键字检索出来
 */
@Component
public class FileSearchTool {
    @Value("${uploadPath}")
    private String uploadPath;
    private final Path indexPath = FileConstant.fullLuceneIndexPath;
    private final Analyzer analyzer;
    private final int showNum = 50;

    @Autowired
    public FileSearchTool(Analyzer analyzer) {
        this.analyzer = analyzer;
    }

    public List<String> searchFiles(String queryText, int maxResults) throws Exception {
        return searchFiles(queryText, null, maxResults);
    }

    public List<String> searchFiles(String queryText, String fileType, int maxResults) throws Exception {
        List<String> results = new ArrayList<>();

        try (Directory dir = FSDirectory.open(indexPath)) {
            IndexReader reader = DirectoryReader.open(dir);

            IndexSearcher searcher = new IndexSearcher(reader);
            QueryParser parser = new QueryParser("content", analyzer);
            Query query = parser.parse(queryText);

            BooleanQuery.Builder builder = new BooleanQuery.Builder();
            builder.add(query, BooleanClause.Occur.MUST);

            if (StrUtil.isEmpty(fileType)) {
                builder.add(new TermQuery(new Term("fileType", fileType.toLowerCase())), BooleanClause.Occur.FILTER);
            }

            TopDocs docs = searcher.search(builder.build(), maxResults);
            for (ScoreDoc scoreDoc : docs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                results.add(doc.get("path"));
            }
        }
        return results;
    }

    public List<SearchBO> searchFilesWithDetails(String queryText) throws Exception {
        return searchFilesWithDetails(queryText, null, 99);
    }

    /**
     * 根据输入的文字，文件类型，查询条件进行文件检索
     * @param queryText
     * @param fileType
     * @param maxResults
     * @return
     * @throws Exception
     */
    public List<SearchBO> searchFilesWithDetails(String queryText, String fileType, int maxResults) throws Exception {
        List<SearchBO> results = new ArrayList<>();
        int sort = 0;
        try (Directory dir = FSDirectory.open(indexPath)) {
            IndexReader reader = DirectoryReader.open(dir);

            IndexSearcher searcher = new IndexSearcher(reader);
            QueryParser parser = new QueryParser("content", analyzer);
            Query query = parser.parse(queryText);

            // 创建高亮器
            Formatter formatter = new SimpleHTMLFormatter("<span style='color:red;font-weight:bold'>", "</span>");
            QueryScorer scorer = new QueryScorer(query);
            Highlighter highlighter = new Highlighter(formatter, scorer);
            highlighter.setTextFragmenter(new SimpleSpanFragmenter(scorer, showNum));

            BooleanQuery.Builder builder = new BooleanQuery.Builder();
            builder.add(query, BooleanClause.Occur.MUST);

            if (!StrUtil.isEmpty(fileType)) {
                builder.add(new TermQuery(new Term("fileType", fileType.toLowerCase())), BooleanClause.Occur.FILTER);
            }

            TopDocs docs = searcher.search(builder.build(), maxResults);
            for (ScoreDoc scoreDoc : docs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                // 获取原始内容
                IndexableField contentField = doc.getField("content");
                String content = contentField != null ? contentField.stringValue() : "";

                // 高亮片段
                String highlightedFragment = getHighlightedFragment(highlighter, content);

                String filePath = doc.get("path");
                String filePdfPath = getFilePdfPath(filePath);
                filePath = filePath.replace("\\", "/").replace(uploadPath, "");
                filePdfPath = filePdfPath.replace("\\", "/").replace(uploadPath, "");
                results.add(SearchBO.builder()
                        .fileOrTableName(doc.get("fileName"))
                        .fileTypeOrColumnName(getFileType("fileType"))
                        .filePdfPath(filePdfPath)
                        .fileOrColumnValue(highlightedFragment)
                        .fileOrTableUrl(filePath)
                        .sort(sort)
                        .isFile(true)
                        .fileSuffix(filePath.substring(filePath.lastIndexOf(".") + 1))
                        .build());
                sort++;
            }
        }
        return results;
    }

    /**
     * 高亮处理
     * @param highlighter
     * @param content
     * @return
     */
    public String getHighlightedFragment(Highlighter highlighter, String content) {
        String result = content.length() > showNum ? content.substring(0, showNum)+"..." : content;
        try {
            try (TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(content))) {
                // 获取最佳高亮片段
                String fragment = highlighter.getBestFragments(tokenStream, content, 2, "...");
                return StrUtil.isEmpty(fragment) ? result : fragment;
            }
        } catch (Exception e) {
            return result;
        }
    }

    /**
     * 获取文件PDF地址
     * @param filePath
     * @return
     */
    private String getFilePdfPath(String filePath) {
        int biasIndex = filePath.lastIndexOf(File.separator);
        if (biasIndex == -1) {
            return "";
        }
        String inputFileName = filePath.substring(biasIndex + 1);
        String inputFileDir = filePath.substring(0, biasIndex).replace("sys", FileConstant.searchPDF);

        int dotIndex = inputFileName.lastIndexOf(".");
        return (dotIndex == -1) ? "" : inputFileDir + File.separator + inputFileName.substring(0, dotIndex) + ".pdf";
    }

    private String getFileType(String suffix) {
        switch (suffix) {
            case "pdf":
                return "pdf";
            case "txt":
            case "doc":
            case "docx":
                return "word";
            case "xlsx":
            case "xls":
                return "excel";
            case "jpg":
            case "jpeg":
            case "png":
                return "image";
            default:
                return "data";
        }
    }
}
